<?php

namespace service\impl;

use service\exception\ServiceException;
use service\ext\PageData;
use service\service\SubjectService;
use service\vo\subject\Subject;
use service\vo\subject\SubjectExt;
use service\vo\subject\SubjectExtBind;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;

class SubjectServiceImpl extends BaseImpl implements SubjectService
{
    //当前模块名称
    private static string $modeName = 'subject';

    /**
     * @inheritDoc
     */
    public function addSubject(Subject $subject): int
    {
        // TODO: Implement addSubject() method.
        $data = [
            self::$FIELD_SUBJECT_LAN_DIR => $subject->getLanDir(),
            self::$FIELD_SUBJECT_RATE => $subject->getRate(),
            self::$FIELD_SUBJECT_TERRITORY_ID => $subject->getTerritoryId(),
            self::$FIELD_SUBJECT_SOURCE_TEXT => $subject->getSourceText(),
            self::$FIELD_SUBJECT_TRANSLATION_TEXT => $subject->getTranslationText(),
            self::$FIELD_SUBJECT_FROM => $subject->getFrom(),
            self::$FIELD_SUBJECT_U_ID => $subject->getUserId(),
            self::$FIELD_SUBJECT_U_TYPE => $subject->getUserType(),
            self::$FIELD_SUBJECT_ADD_TIME => date('Y-m-d H:i:s'),
        ];

        return $this->getSubjectDb()->insertGetId($data);
    }

    /**
     * @inheritDoc
     */
    public function addSubjects(array $subjects): int
    {
        // TODO: Implement addSubjects() method.
        $data = [];
        foreach ($subjects as $subject) {
            $data[] = [
                self::$FIELD_SUBJECT_LAN_DIR => $subject->getLanDir(),
                self::$FIELD_SUBJECT_RATE => $subject->getRate(),
                self::$FIELD_SUBJECT_TERRITORY_ID => $subject->getTerritoryId(),
                self::$FIELD_SUBJECT_SOURCE_TEXT => $subject->getSourceText(),
                self::$FIELD_SUBJECT_TRANSLATION_TEXT => $subject->getTranslationText(),
                self::$FIELD_SUBJECT_FROM => $subject->getFrom(),
                self::$FIELD_SUBJECT_U_ID => $subject->getUserId(),
                self::$FIELD_SUBJECT_U_TYPE => $subject->getUserType(),
                self::$FIELD_SUBJECT_ADD_TIME => date('Y-m-d H:i:s'),
            ];
        }
        return $this->getSubjectDb()->insertAll($data);
    }

    /**
     * @inheritDoc
     * @throws DbException
     */
    public function editSubject(Subject $subject): int
    {
        // TODO: Implement editSubject() method.
        $id = $subject->getId();
        $data = [
            self::$FIELD_SUBJECT_LAN_DIR => $subject->getLanDir(),
            self::$FIELD_SUBJECT_RATE => $subject->getRate(),
            self::$FIELD_SUBJECT_TERRITORY_ID => $subject->getTerritoryId(),
            self::$FIELD_SUBJECT_SOURCE_TEXT => $subject->getSourceText(),
            self::$FIELD_SUBJECT_TRANSLATION_TEXT => $subject->getTranslationText(),
            self::$FIELD_SUBJECT_FROM => $subject->getFrom(),
        ];
        return $this->getSubjectDb()->where(self::$FIELD_COMMON_ID, $id)->update($data);
    }

    /**
     * @inheritDoc
     * @throws DbException
     */
    public function deleteSubjectById(int $id): int
    {
        // TODO: Implement deleteSubjectById() method.
        return $this->getSubjectDb()->where(self::$FIELD_COMMON_ID, $id)->delete();
    }

    /**
     * @inheritDoc
     * @throws DbException
     * @throws ServiceException
     */
    public function getSubjectById(int $id): Subject
    {
        // TODO: Implement getSubjectById() method.
        $po = $this->getSubjectDb()->where(self::$FIELD_COMMON_ID, $id)->find();
        if (empty($po)) {
            throw new ServiceException(self::$modeName, self::ERROR_SUBJECT_NOT_EXISTS);
        }
        return $this->getSubjectVoFromPo($po);
    }

    /**
     * @inheritDoc
     * @throws DbException
     */
    public function getSubjectByIds(array $ids): array
    {
        // TODO: Implement getSubjectByIds() method.
        $voArray = [];
        $poArr = $this->getSubjectDb()
            ->whereIn(self::$FIELD_COMMON_ID, $ids)
            ->select();
        foreach ($poArr as $po) {
            $voArray[] = $this->getSubjectVoFromPo($po);
        }
        return $voArray;
    }

    /**
     * @inheritDoc
     * @throws DbException
     */
    public function searchSubjects(int $currentPage, int $pageSize, array $filterCriteria, array $sorterCriteria): PageData
    {
        // TODO: Implement searchSubjects() method.
        $filter = [];
        if (!empty($filterCriteria)) {
            foreach ($filterCriteria as $key => $value) {
                if (strcasecmp($key, self::$FILTER_LAN_DIR) == 0) {
                    //语向
                    $filter[] = [self::$FIELD_SUBJECT_LAN_DIR, "=", $value];
                } else if (strcasecmp($key, self::$FILTER_RATE) == 0) {
                    //难度系数
                    $filter[] = [self::$FIELD_SUBJECT_RATE, "=", $value];
                } else if (strcasecmp($key, self::$FILTER_TERRITORY_ID) == 0) {
                    //领域id
                    $filter[] = [self::$FIELD_SUBJECT_TERRITORY_ID, "=", $value];
                } else if (strcasecmp($key, self::$FILTER_SOURCE_TEXT) == 0) {
                    //原文
                    $filter[] = [self::$FIELD_SUBJECT_SOURCE_TEXT, "like", "%{$value}%"];
                } else if (strcasecmp($key, self::$FILTER_USER_ID) == 0) {
                    //创建者id
                    $filter[] = [self::$FIELD_SUBJECT_U_ID, "=", $value];
                } else if (strcasecmp($key, self::$FILTER_USER_TYPE) == 0) {
                    //创建者类型
                    $filter[] = [self::$FIELD_SUBJECT_U_TYPE, "=", $value];
                } elseif (strcasecmp($key, self::$FILTER_ADD_TIME) == 0) {
                    //时间
                    $betweenTime = makeBetween($value['start'], $value['end']);
                    $filter[] = [self::$FIELD_SUBJECT_ADD_TIME, $betweenTime[0], $betweenTime[1]];
                }
            }
        }

        //构建排序条件
        //是否取随机数据
        $isRand = false;
        $sort = [];
        if (!empty($sorterCriteria)) {
            foreach ($sorterCriteria as $key => $value) {
                if (strcasecmp($key, self::$SORT_ID) == 0) {
                    $sort[self::$FIELD_COMMON_ID] = $value ? self::$SORT_VALUE_ASC : self::$SORT_VALUE_DESC;
                } else if(strcasecmp($key, self::$SORT_RAND) == 0){
                    $isRand = true;
                }
            }
        }

        $count = $this->getSubjectDb()
            ->where($filter)
            ->count();
        if ($isRand) {
            $list = $this->getSubjectDb()
                ->where($filter)
                ->orderRaw("rand()")
                ->limit($pageSize)
                ->page($currentPage)
                ->select();
        } else {
            $list = $this->getSubjectDb()
                ->where($filter)
                ->order($sort)
                ->limit($pageSize)
                ->page($currentPage)
                ->select();
        }


        $dataList = [];
        foreach ($list as $po) {
            $dataList[] = $this->getSubjectVoFromPo($po);
        }

        $pageData = new PageData();
        $pageData->setData($dataList);
        $pageData->setCount($count);

        return $pageData;
    }

    /**
     * @inheritDoc
     */
    public function addSubjectExt(SubjectExt $subjectExt): int
    {
        // TODO: Implement addSubjectExt() method.
        //判断名称是否存在
        $po = $this->getSubjectExtDb()
            ->where(self::$FIELD_SUBJECT_EXT_TITLE, $subjectExt->getTitle())
            ->find();
        if (!empty($po)) {
            throw new ServiceException(self::$modeName, self::ERROR_SUBJECT_EXT_TITLE_EXISTS);
        }
        $data = [
            self::$FIELD_SUBJECT_EXT_TYPE => $subjectExt->getType(),
            self::$FIELD_SUBJECT_EXT_TITLE => $subjectExt->getTitle(),
            self::$FIELD_SUBJECT_EXT_CONTENT => $subjectExt->getContent(),
            self::$FIELD_SUBJECT_EXT_ADD_TIME => date("Y-m-d H:i:s"),
        ];
        return $this->getSubjectExtDb()->insertGetId($data);
    }

    /**
     * @inheritDoc
     */
    public function addSubjectExits(array $subjectExits): int
    {
        // TODO: Implement addSubjectExits() method.
        $data = [];
        foreach ($subjectExits as $subjectExit) {
            $data[] = [
                self::$FIELD_SUBJECT_EXT_TYPE => $subjectExit->getType(),
                self::$FIELD_SUBJECT_EXT_TITLE => $subjectExit->getTitle(),
                self::$FIELD_SUBJECT_EXT_CONTENT => $subjectExit->getContent(),
                self::$FIELD_SUBJECT_EXT_ADD_TIME => date("Y-m-d H:i:s")
            ];
        }
        return $this->getSubjectExtDb()->insertAll($data);
    }

    /**
     * @inheritDoc
     * @throws DbException
     * @throws ServiceException
     */
    public function getSubjectExtById(int $id): SubjectExt
    {
        // TODO: Implement getSubjectExtById() method.
        $po = $this->getSubjectExtDb()
            ->where(self::$FIELD_COMMON_ID, $id)
            ->find();
        if (empty($po)) {
            throw new ServiceException(self::$modeName, self::ERROR_SUBJECT_EXT_NOT_EXISTS);
        }
        return $this->getSubjectExtVoFromPo($po);
    }

    /**
     * @inheritDoc
     * @throws DbException
     */
    public function getSubjectExtByIds(array $ids): array
    {
        // TODO: Implement getSubjectExtByIds() method.
        $voArr = [];
        $poArr = $this->getSubjectExtDb()
            ->whereIn(self::$FIELD_COMMON_ID, $ids)
            ->select();
        foreach ($poArr as $po) {
            $voArr[] = $this->getSubjectExtVoFromPo($po);
        }
        return $voArr;
    }

    /**
     * @inheritDoc
     * @throws DbException
     */
    public function deleteSubjectExtById(int $id): int
    {
        // TODO: Implement deleteSubjectExtById() method.
        return $this->getSubjectExtDb()->delete($id);
    }

    /**
     * @inheritDoc
     * @throws DbException
     */
    public function deleteSubjectExtByIds(array $ids): int
    {
        // TODO: Implement deleteSubjectExtByIds() method.
        return $this->getSubjectExtDb()->delete($ids);
    }

    /**
     * @inheritDoc
     */
    public function addSubjectExtBind(SubjectExtBind $subjectExtBind): int
    {
        // TODO: Implement addSubjectExtBind() method.
        $data = [
            self::$FIELD_COMMON_SUBJECT_ID => $subjectExtBind->getSubjectId(),
            self::$FIELD_SUBJECT_EXT_BIND_EXT_ID => $subjectExtBind->getSubjectExtId(),
            self::$FIELD_SUBJECT_EXT_BIND_ADD_TIME => date("Y-m-d H:i:s"),
        ];
        return $this->getSubjectExtBindDb()->insert($data);
    }

    /**
     * @inheritDoc
     */
    public function addSubjectExtBinds(array $subjectExtBinds): int
    {
        // TODO: Implement addSubjectExtBinds() method.
        $data = [];
        foreach ($subjectExtBinds as $subjectExtBind) {
            $data[] = [
                self::$FIELD_COMMON_SUBJECT_ID => $subjectExtBind->getSubjectId(),
                self::$FIELD_SUBJECT_EXT_BIND_EXT_ID => $subjectExtBind->getSubjectExtId(),
                self::$FIELD_SUBJECT_EXT_BIND_ADD_TIME => date("Y-m-d H:i:s"),
            ];
        }
        return $this->getSubjectExtBindDb()->insertAll($data);
    }

    /**
     * @inheritDoc
     * @throws DbException
     */
    public function deleteSubjectExtBindById(int $id): int
    {
        // TODO: Implement deleteSubjectExtBindById() method.
        return $this->getSubjectExtBindDb()->delete($id);
    }

    /**
     * @inheritDoc
     * @throws DbException
     */
    public function deleteSubjectExtBindByIds(array $ids): int
    {
        // TODO: Implement deleteSubjectExtBindByIds() method.
        return $this->getSubjectExtBindDb()->delete($ids);
    }

    /**
     * @inheritDoc
     * @throws ServiceException
     */
    public function getSubjectExtBindById(int $id): SubjectExtBind
    {
        // TODO: Implement getSubjectExtBindById() method.
        $po = $this->getSubjectExtBindDb()
            ->where(self::$FIELD_COMMON_ID, $id)
            ->findOrEmpty();
        if ($po->isEmpty()) {
            throw new ServiceException(self::$modeName, self::ERROR_SUBJECT_EXT_BIND_NOT_EXISTS);
        }

        return $this->getSubjectExtBindVoFromPo($po);
    }

    /**
     * @inheritDoc
     * @throws DbException
     */
    public function getSubjectExtBindByIds(int $ids): array
    {
        // TODO: Implement getSubjectExtBindByIds() method.
        $voArr = [];
        $poArr = $this->getSubjectExtBindDb()
            ->whereIn(self::$FIELD_COMMON_ID, $ids)
            ->select();
        foreach ($poArr as $po) {
            $voArr[] = $this->getSubjectExtBindVoFromPo($po);
        }
        return $voArr;
    }

    private function getSubjectVoFromPo($po) : Subject
    {
        $vo = new Subject();
        $vo->setId($po[self::$FIELD_COMMON_ID]);
        $vo->setLanDir($po[self::$FIELD_SUBJECT_LAN_DIR]);
        $vo->setRate($po[self::$FIELD_SUBJECT_RATE]);
        $vo->setTerritoryId($po[self::$FIELD_SUBJECT_TERRITORY_ID]);
        $vo->setSourceText($po[self::$FIELD_SUBJECT_SOURCE_TEXT]);
        $vo->setTranslationText($po[self::$FIELD_SUBJECT_TRANSLATION_TEXT]);
        $vo->setFrom($po[self::$FIELD_SUBJECT_FROM]);
        $vo->setUserId($po[self::$FIELD_SUBJECT_U_ID]);
        $vo->setUserType($po[self::$FIELD_SUBJECT_U_TYPE]);
        $vo->setAddTime($po[self::$FIELD_SUBJECT_ADD_TIME]);
        return $vo;
    }

    private function getSubjectExtVoFromPo($po) : SubjectExt
    {
        $vo = new SubjectExt();
        $vo->setId($po[self::$FIELD_COMMON_ID]);
        $vo->setAddTime($po[self::$FIELD_SUBJECT_EXT_ADD_TIME]);
        $vo->setTitle($po[self::$FIELD_SUBJECT_EXT_TITLE]);
        $vo->setType($po[self::$FIELD_SUBJECT_EXT_TYPE]);
        $vo->setContent($po[self::$FIELD_SUBJECT_EXT_CONTENT]);
        return $vo;
    }

    private function getSubjectExtBindVoFromPo($po) : SubjectExtBind
    {
        $vo = new SubjectExtBind();
        $vo->setId($po[self::$FIELD_COMMON_ID]);
        $vo->setAddTime($po[self::$FIELD_SUBJECT_EXT_BIND_ADD_TIME]);
        $vo->setSubjectId($po[self::$FIELD_COMMON_SUBJECT_ID]);
        $vo->setSubjectExtId($po[self::$FIELD_SUBJECT_EXT_BIND_EXT_ID]);
        $vo->setType($po[self::$FIELD_SUBJECT_EXT_BIND_TYPE]);
        return $vo;
    }

    /**
     * @throws DbException
     * @throws ServiceException
     */
    public function editSubjectExt(SubjectExt $subjectExt): int
    {
        // TODO: Implement editSubjectExt() method.
        $id = $subjectExt->getId();

        //判断名称是否存在
        $po = $this->getSubjectExtDb()
            ->where(self::$FIELD_SUBJECT_EXT_TITLE, $subjectExt->getTitle())
            ->where(self::$FIELD_COMMON_ID, '<>', $id)
            ->find();
        if(!empty($po)){
            throw new ServiceException(self::$modeName, self::ERROR_SUBJECT_EXT_TITLE_EXISTS);
        }
        $data = [
            self::$FIELD_SUBJECT_EXT_TYPE => $subjectExt->getType(),
            self::$FIELD_SUBJECT_EXT_TITLE => $subjectExt->getTitle(),
            self::$FIELD_SUBJECT_EXT_CONTENT => $subjectExt->getContent()
        ];

        return $this->getSubjectExtDb()->where(self::$FIELD_COMMON_ID, $id)->update($data);
    }

    /**
     * @throws ModelNotFoundException
     * @throws DataNotFoundException
     * @throws DbException
     */
    public function searchSubjectExt(int $currentPage, int $pageSize, array $filterCriteria, array $sorterCriteria): PageData
    {
        // TODO: Implement searchSubjectExt() method.
        $filter = [];
        if (!empty($filterCriteria)) {
            foreach ($filterCriteria as $key => $value) {
                if (strcasecmp($key, self::$FILTER_TYPE) == 0) {
                    //语向
                    $filter[] = [self::$FIELD_SUBJECT_EXT_TYPE, "=", $value];
                } else if (strcasecmp($key, self::$FILTER_TITLE) == 0) {
                    //语向
                    $filter[] = [self::$FIELD_SUBJECT_EXT_TITLE, "like", "%{$value}%"];
                } elseif (strcasecmp($key, self::$FILTER_ADD_TIME) == 0) {
                    //时间
                    $betweenTime = makeBetween($value['start'], $value['end']);
                    $filter[] = [self::$FIELD_SUBJECT_EXT_ADD_TIME, $betweenTime[0], $betweenTime[1]];
                } else if (strcasecmp($key, self::$FILTER_NOT_ID_IN) == 0 && is_array($value)) {
                    $filter[] = [self::$FIELD_COMMON_ID, "NOT IN", $value];
                }
            }
        }

        //构建排序条件
        //是否取随机数据
        $isRand = false;
        $sort = [];
        if (!empty($sorterCriteria)) {
            foreach ($sorterCriteria as $key => $value) {
                if (strcasecmp($key, self::$SORT_ID) == 0) {
                    $sort[self::$FIELD_COMMON_ID] = $value ? self::$SORT_VALUE_ASC : self::$SORT_VALUE_DESC;
                }
            }
        }

        $count = $this->getSubjectExtDb()
            ->where($filter)
            ->count();
        $list = $this->getSubjectExtDb()
            ->where($filter)
            ->order($sort)
            ->limit($pageSize)
            ->page($currentPage)
            ->select();

        $dataList = [];
        foreach ($list as $po) {
            $dataList[] = $this->getSubjectExtVoFromPo($po);
        }

        $pageData = new PageData();
        $pageData->setData($dataList);
        $pageData->setCount($count);

        return $pageData;
    }


    /**
     * @throws DbException
     */
    public function deleteSubjectExtBindByExtId(int $extId): int
    {
        // TODO: Implement deleteSubjectExtBindByExtId() method.
        return $this->getSubjectExtBindDb()->where(self::$FIELD_SUBJECT_EXT_BIND_EXT_ID, $extId)->delete();
    }

    /**
     * @throws ModelNotFoundException
     * @throws DataNotFoundException
     * @throws DbException
     */
    public function getSubjectExtBindsBySubjectId(int $subjectId): array
    {
        // TODO: Implement getSubjectExtBindsBySubjectId() method.
        $poArr = $this->getSubjectExtBindDb()->where(self::$FIELD_COMMON_SUBJECT_ID, $subjectId)->select();
        $voArr = [];
        foreach ($poArr as $po) {
            $voArr[] = $this->getSubjectExtBindVoFromPo($po);
        }
        return $voArr;
    }

    /**
     * @throws ModelNotFoundException
     * @throws DbException
     * @throws DataNotFoundException
     */
    public function getSubjectExtByTitle(string $title): SubjectExt | null
    {
        // TODO: Implement getSubjectExtByTitle() method.
        $po = $this->getSubjectExtDb()
            ->where(self::$FIELD_SUBJECT_EXT_TITLE, $title)
            ->find();
        if (empty($po)) {
            return null;
        }

        return $this->getSubjectExtVoFromPo($po);
    }

    public function addSubjectExtBindsBySubjectIdAndExtIds(int $subjectId, array $extIds): int
    {
        // TODO: Implement addSubjectExtBindsBySubjectIdAndExtIds() method.
        $data = [];
        foreach ($extIds as $extId) {
            $data[] = [
                self::$FIELD_COMMON_SUBJECT_ID => $subjectId,
                self::$FIELD_SUBJECT_EXT_BIND_EXT_ID => $extId,
                self::$FIELD_SUBJECT_EXT_BIND_ADD_TIME => date("Y-m-d H:i:s"),
            ];
        }
        return $this->getSubjectExtBindDb()->insertAll($data);
    }

    /**
     * @throws DbException
     */
    public function setSubjectExtBindTypeById(int $id, string $type): int
    {
        // TODO: Implement setSubjectExtBindTypeById() method.
        return $this->getSubjectExtBindDb()
            ->where(self::$FIELD_COMMON_ID, $id)
            ->update([
                self::$FIELD_SUBJECT_EXT_BIND_TYPE => $type
            ]);
    }
}